按照SIP标准,jssip.ua有必须和可选两种参数需要配置。例如:
var configuration = {
'ws_servers': 'wss://sip-ws.example.com',
'uri': 'sip:alice@example.com',
'password': 'superpassword'
};
var coolPhone = new JsSIP.UA(configuration);
下面分别说明各参数的含义和格式。
1)必须的参数
uri
用户的SIP URI地址(即SIP账号),格式类似Email(即“协议:名称@主机”),例如:
ws_servers
WebSocket服务器地址(WS URI),格式类似HTTP URL,例如:wss://192.168.4.224:8089/ws
2)可选的参数
authorization_user
字符串,指生成身份验证凭据时使用的用户名。如果未定义,则使用URI参数中的值。例如:authorization_user: 授权用户名
log
字符型,表示日志级别,取值为err、info或debug。例如:log: { level: 'err' }
connection_recovery_max_interval
整型,表示WebSocket重新连接最小尝试秒数。
connection_recovery_min_interval
整型,表示WebSocket重新连接最大尝试秒数。
display_name
字符串,指在被叫方的呼叫请求或发送即时消息中显示的描述性的名称。例如:display_name: myname
hack_ip_in_contact
布尔型,表示将随机IP地址设置为头字段中的主机值,并通过参数发送。用于SIP注册不允许的URI。有效值为true和false(布尔)。默认值为false。
hack_via_tcp
布尔型,表示是否将SIP的请求的传输协议设置为TCP,当不准备解析头部“WS”或“WSS”时有用。有效值为true和false(布尔)。默认值为false。
hack_via_ws
布尔型,当连接到一个安全的WebSocket服务器时(“WSS://”),指示是“通过WS而不是WSS”。当穿越HTTP / WebSocket代理作为TLS隧道时有用。有效值为true或false。默认值为false。
instance_id
no_answer_timeout
整型,表示呼叫请求的超时等待时间(单位:秒)。如果超过此时间没有应答,则自动结束通话请求。默认值是60。例如:no_answer_timeout: 45
session_timers
布尔型,是否启用会话计时器(RFC 4028),默认值是true。
node_websocket_options
password
字符串,表示SIP授权密码。例如:password: sip_password
register
布尔型,表示JsSIP用户代理启动时是否自动注册,有效值为true和false(默认值为true)。例如:register: true
register_expires
整型,表示注册超时时间(单位:秒),默认值是600。例如:register_expires: 30
registrar_server
字符串,指SIP注册服务器的URI,有效值是一个没有用户名的SIP URI,默认值是NULL(表示注册URI是从用户的SIP URI参数提取的)。例如:registrar_server: 'sip:registrar.mydomain.com'
use_preloaded_route
布尔型,如果设置为TRUE,则在发送每个SIP会话的请求时由JsSIP包装一个相关联的WebSocket服务器作为SIP URI路由头。一些SIP Outbound代理要求这样的header。有效值为true和false(布尔)。默认值为false。
基本功能接口的函数原型参见“gui.js”文件,主要提供用户登录,拨号,挂断等功能。
创建JSSIP.UA
| 原型 | JsSIP.UA(configuration); |
|---|---|
| 参数 | configuration |
| 返回值 | JsSIP.UA对象。 |
| 说明 | 初始化JSSIP客户端代理 |
| 示例 |
configuration = {
log: { level: 'err' },//log级别debug jerry
uri: sip_uri,
password: sip_password,
ws_servers: ws_servers,
display_name: display_name,
authorization_user: authorization_user,
register: register,
register_expires: register_expires,
registrar_server: registrar_server,
no_answer_timeout: no_answer_timeout,
session_timers: session_timers,
use_preloaded_route: use_preloaded_route,
connection_recovery_min_interval: connection_recovery_min_interval,
connection_recovery_max_interval: connection_recovery_max_interval,
hack_via_tcp: hack_via_tcp,
hack_via_ws: hack_via_ws,
hack_ip_in_contact: hack_ip_in_contact
};
}
try {
ua = new JsSIP.UA(configuration);
} catch(e) {
console.log(e.toString());
Y_U_NO(e.message, 4000);
return;
}
|
启动WebSocket连接
| 函数原型 | ua.start(); |
|---|---|
| 参数 | 无。 |
| 返回值 | 无 |
| 说明 | W3CWebSocket创建,注册回调等。 |
| 示例 |
ua.start();
|
打开(关闭)调试信息
| 函数原型 | JsSIP.debug.enable('JsSIP:*'); JsSIP.debug.disable('JsSIP:*'); |
|---|---|
| 参数 | 无 |
| 返回值 | 无。 |
| 说明 | 在浏览器控制台输出调试信息。 |
| 示例 |
JsSIP.debug.enable('JsSIP:*');
可以在首页
|
JsSIP的每一个事件处理程序是通过一个字符串参数值触发的,而这个字符串要用相同的对象或实例的侦听器通过on()方法安装。例如:
var ua = new JsSIP.UA( ... );
ua.on('newRTCSession', function(data) {
var originator = data.originator;
var session = data.session;
var request = data.request;
// Stop the UA. Note that `this` is bound to the `ua` instance.
this.stop();
});
JS事件主要分为用户代理事件(ua)和会话状态事件(call)。其中,前者主要用于报告用户账号的登录状态,后者主要用于报告通话的进行状态。
用户代理事件
Connected事件
| 原型 | ua.on('connected', function(e) {…}); |
|---|---|
| 参数 | 'connected':字符串,表示已经连接到websocket服务器。 function(e) {…}:回调函数,连接上以后界面做一些处理。 |
| 返回值 | 无 |
| 说明 | 如果JSSIP连接上websocket服务器,会触发此回调 |
| 示例 |
// Transport connection/disconnection callbacks
ua.on('connected', function(e) {
document.title = PageTitle;
GUI.setStatus("connected");
// Habilitar el phone.
$("#phone .controls .ws-disconnected").hide();
ws_was_connected = true;
});
|
Disconnected事件
| 原型 | ua.on('disconnected', function(e) {…}); |
|---|---|
| 参数 | 'disconnected':字符串,表示断开连接到websocket服务器。 function(e) {…}:回调函数,断开以后界面做一些处理。 |
| 返回值 | 无。 |
| 说明 | 如果JSSIP断开连接websocket服务器,会触发此回调。 |
| 示例 |
ua.on('disconnected', function(e) {
document.title = PageTitle;
GUI.setStatus("disconnected");
// Deshabilitar el phone.
$("#phone .controls .ws-disconnected").show();
// Eliminar todas las sessiones existentes.
$("#sessions > .session").each(function(i, session) {
GUI.removeSession(session, 500);
});
if (! ws_was_connected) {
//alert("WS connection error:\n\n- WS close code: " + e.data.code + "\n- WS close reason: " + e.data.reason);
console.error("WS connection error | WS close code: " + e.code + " | WS close reason: " + e.reason);
//if (! window.CustomJsSIPSettings) { window.location.reload(false); }
}
});
|
newRTCSession事件
| 原型 | ua.on('newRTCSession', function(e) {…}); |
|---|---|
| 参数 | 'newRTCSession':字符串,表示新的会话产生。 function(e) {…}:回调,请在此做新会话的监听工作。 |
| 返回值 | 无 |
| 说明 | 新的会话(Session)将会发出一些事件,在回调里应该做监听处理,后面会说明会话(Session)需要监听那些事件。 |
| 示例 |
// Call/Message reception callbacks
ua.on('newRTCSession', function(e) {
// Set a global '_Session' variable with the session for testing.
_Session = e.session;
GUI.new_session(e)
});
|
registered事件
| 原型 | ua.on('registered', function(e){…}); |
|---|---|
| 参数 | 'registered': 字符串,表示已经注册到websocket服务器上 function(e){…}: |
| 返回值 | 无 |
| 说明 | 客户端已经注册上服务器,可以随时发送SIP报文 |
| 示例 |
ua.on('registered', function(e){
console.info('Registered');
GUI.setStatus("registered");
if (invitedBy) {
input[type='text']").val("Hi there, you have invited me to call you :)");
phone_dialed_number_screen.val(invitedBy);
phone_chat_button.click();
var invited_session = GUI.getSession("sip:" + invitedBy + "@" + tryit_sip_domain);
invitedBy = null;
$(invited_session).find(".chat > input[type='text']").val("Hi there, wanna talk?");
var e = jQuery.Event("keydown");
e.which = 13 // Enter
$(invited_session).find(".chat > input[type='text']").trigger(e);
$(invited_session).find(".chat > input[type='text']").focus();
}
});
|
unregistered事件
| 原型 | ua.on('unregistered', function(e){…}); |
|---|---|
| 参数 | 'unregistered':字符串,表示反注册事件。 function(e){…}:回调,反注册成功后在此显示一些提示 |
| 返回值 | 无 |
| 说明 | 请在此处进行反注册后的回调处理 |
| 示例 |
ua.on('unregistered', function(e){
console.info('Deregistered');
GUI.setStatus("connected");
});
|
registrationFailed事件
| 原型 | ua.on('registrationFailed', function(e) {…}); |
|---|---|
| 参数 | 'registrationFailed':字符串,表示注册失败。 |
| 返回值 | 无 |
| 说明 | 注册失败时回调 |
| 示例 |
ua.on('registrationFailed', function(e) {
console.info('Registration failure');
GUI.setStatus("connected");
if (! e.response) {
// alert("SIP registration error:\n" + e.data.cause);
}
else {
// alert("SIP registration error:\n" + e.data.response.status_code.toString() + " " + e.data.response.reason_phrase)
}
// if (! window.CustomJsSIPSettings) { window.location.reload(false); }
});
|
会话状态事件
Progress事件
| 原型 | call.on('progress',function(e){…}); |
|---|---|
| 参数 | 'progress':字符串,表示呼叫正在处理。 function(e){…}:回调,可进行界面处理。 |
| 返回值 | 无。 |
| 说明 | 呼叫正在处理,可以显示一些提示信息。 |
| 示例 |
// Progress
call.on('progress',function(e){
if (e.originator === 'remote') {
GUI.setCallSessionStatus(session, 'in-progress');
}
});
|
Accepted事件
| 原型 | call.on('accepted',function(e){…}); |
|---|---|
| 参数 | 'accepted':字符串,表示对方已经应答。 function(e){…}:回调,可以显示一些界面提示。 |
| 返回值 | 无。 |
| 说明 | 如果已经应答,则回调,用来提示通话已经开始。 |
| 示例 |
call.on('accepted',function(e){
var streamIdx, trackIdx, streamsLength, tracksLength, tracks,
localStreams = call.connection.getLocalStreams();
streamsLength = localStreams.length;
//alert(streamsLength);
for (streamIdx = 0; streamIdx < streamsLength; streamIdx++) {
tracks = localStreams[streamIdx].getAudioTracks();
tracksLength = tracks.length;
for (trackIdx = 0; trackIdx < tracksLength; trackIdx++) {
tracks[trackIdx].enabled = false;
}
}
//Attach the streams to the views if it exists.
if (call.connection.getLocalStreams().length > 0) {
localStream = call.connection.getLocalStreams()[0];
selfView = JsSIP.rtcninja.attachMediaStream(selfView, localStream);
selfView.volume = 0;
}
if (e.originator === 'remote') {
if (e.response.getHeader('X-Can-Renegotiate') === 'false') {
call.data.remoteCanRenegotiateRTC = false;
}
else {
call.data.remoteCanRenegotiateRTC = true;
}
}
GUI.setCallSessionStatus(session, 'answered');
});
|
Addstream事件
| 函数原型 | call.on('addstream', function(e) {…}); |
|---|---|
| 参数 | 'addstream':字符串,表示流已经开始接收。 function(e) {…}:回调,收到流以后的处理。 |
| 返回值 | 无 |
| 说明 | 通过此回调把流放到合适的地方,音频自动处理,这里如果不涉及视频可忽略。 |
| 示例 |
call.on('addstream', function(e) {
console.log('Tryit: addstream()');
remoteStream = e.stream;
remoteView = JsSIP.rtcninja.attachMediaStream(remoteView, remoteStream);
});
|
Failed事件
| 函数原型 | call.on('failed',function(e) {…}); |
|---|---|
| 参数 | 'failed':字符串,表示会话产生未知错误; function(e) {…}:回调,请在此处对错误进行简单提示。 |
| 返回值 | 无 |
| 说明 | 开始拨号后产生错误,请在此适当处理 |
| 示例 |
call.on('failed',function(e) {
var
cause = e.cause,
response = e.response;
if (e.originator === 'remote' && cause.match("SIP;cause=200", "i")) {
cause = 'answered_elsewhere';
}
GUI.setCallSessionStatus(session, 'terminated', cause);
soundPlayer.setAttribute("src", "sounds/outgoing-call-rejected.wav");
soundPlayer.play();
GUI.removeSession(session, 1500);
selfView.src = '';
remoteView.src = '';
_Session = null;
});
|
newDTMF事件(保留)
hold事件(保留)
unhold事件(保留)
ended事件
| 函数原型 | call.on('ended',function(e) {…}); |
|---|---|
| 参数 | 'ended':字符串,表示会话结束; function(e) {…}:回调,请在此处进行简单提示。 |
| 返回值 | 无 |
| 说明 | 挂断后在这里做一些提示 |
| 示例 |
call.on('ended', function(e) {
var cause = e.cause;
GUI.setCallSessionStatus(session, "terminated", cause);
GUI.removeSession(session, 1500);
selfView.src = '';
remoteView.src = '';
_Session = null;
JsSIP.rtcninja.closeMediaStream(localStream);
});
|
update事件(保留)
reinvite事件(保留)
注册
| 原型 | ua.register(); |
|---|---|
| 参数 | 空。 |
| 返回值 | 无。 |
| 说明 | 向websocket服务器注册。 |
| 示例 |
ua.register();
|
取消注册
| 原型 | ua.unregister(object); |
|---|---|
| 参数 | Object:json对象,可为空。 |
| 返回值 | 无 |
| 说明 | 向websocket服务器反注册。 |
| 示例 |
ua.unregister({'all': true});
|
静音
| 原型 | tracks[trackIdx].enabled; |
|---|---|
| 说明 | 关闭本地流。 |
| 示例 | var streamIdx, trackIdx, streamsLength, tracksLength, tracks, localStreams = call.connection.getLocalStreams(); streamsLength = localStreams.length; for (streamIdx = 0; streamIdx < streamsLength; streamIdx++) { tracks = localStreams[streamIdx].getAudioTracks(); tracksLength = tracks.length; for (trackIdx = 0; trackIdx < tracksLength; trackIdx++) { tracks[trackIdx].enabled = false; } } |
取消静音
| 原型 | tracks[trackIdx].enabled; |
|---|---|
| 说明 | 打开本地流。 |
| 示例 | var streamIdx, trackIdx, streamsLength, tracksLength, tracks, localStreams = call.connection.getLocalStreams(); streamsLength = localStreams.length; for (streamIdx = 0; streamIdx < streamsLength; streamIdx++) { tracks = localStreams[streamIdx].getAudioTracks(); tracksLength = tracks.length; for (trackIdx = 0; trackIdx < tracksLength; trackIdx++) { tracks[trackIdx].enabled = true; } } |
| 原型 | ua.call(target, {…}); |
|---|---|
| 参数 | target:字符串,会议室号码。 {…}:json对象,用来设置拨号参数,具体请看示例。 |
| 返回值 | 无 |
| 说明 | |
| 示例 |
ua.call(target, {
pcConfig: peerconnection_config,
mediaConstraints: { audio: true,DtlsSrtpKeyAgreement: false, video:false },
extraHeaders: [
'X-Can-Renegotiate: ' + String(localCanRenegotiateRTC())
],
rtcOfferConstraints: {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1,
DtlsSrtpKeyAgreement: 0
}
});
注:peerconnection_config 可以这样写:{ "iceServers": [], "gatheringTimeout": 2000 },参考DEMO。
|
| 原型 | session.call.terminate(); |
|---|---|
| 参数 | 无。 |
| 返回值 | 无 |
| 说明 | 挂断是通过会话session调用dialogs完成的。 |
| 示例 |
button_hangup.click(function() {
GUI.setCallSessionStatus(session, "terminated", "terminated");
session.call.terminate();
GUI.removeSession(session, 500);
});
|
下面红色字体部分需要添加到HTML页面中,这些JS文件主要实现sip协议的封装、事件注册和界面提示功能,例如:拨号,挂断等。
//打开调试,关闭请用JsSIP.debug.disable("*")
//下面判断浏览器是否支持 WebRTC 以及浏览器适配,请根据需要添加提示信息
if (window.rtcninjaTemasys) {
rtcninjaTemasys({},
// alreadyInstalledCb
function() {
JsSIP.rtcninja({plugin: rtcninjaTemasys});
},
// needInstallCb
function(data) {
alert('WebRTC plugin required');
},
// notRequiredCb
null
);
}